/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ola_base_utils.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "ola_pal_os.h"
#include "ola_log.h"
#include "ola_sdk_api.h"
#include "ola_error.h"
#include "securec.h"

#define OLA_ONE_HEX_TO_STR_LEN 2

void OlaPutNullAfterFree(void **ptr)
{
    if ((ptr != NULL) && (*ptr != NULL)) {
        OlaFree(*ptr);
        *ptr = NULL;
    }
}

char *OlaGetJsonString(const cJSON *json, const char *key)
{
    if ((json == NULL) || (key == NULL)) {
        OlaLogWarning("invalid param");
        return NULL;
    }
    cJSON *obj = cJSON_GetObjectItem(json, key);
    if ((obj == NULL) || (!cJSON_IsString(obj)) || (obj->valuestring == NULL)) {
        OlaLogWarning("json get [%s] str err\r\n", key);
        return NULL;
    }
    return obj->valuestring;
}

int OlaGetJsonNumber(const cJSON *json, const char *key, int *value)
{
    if ((json == NULL) || (key == NULL) || (value == NULL)) {
        OlaLogWarning("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }
    cJSON *obj = cJSON_GetObjectItem(json, key);
    if ((obj == NULL) || (!cJSON_IsNumber(obj))) {
        OlaLogWarning("json get [%s] number err\r\n", key);
        return OLA_CJSON_ERR_GET_NUMBER;
    }
    *value = obj->valueint;
    return OLA_OK;
}

char *OlaCopyJsonString(const cJSON *object, const char *name)
{
    if ((object == NULL) || (name == NULL)) {
        OlaLogError("invalid param");
        return NULL;
    }
    char *tmp = OlaGetJsonString(object, name);
    if (tmp == NULL) {
        OlaLogError("get item %s failed", name);
        return NULL;
    }
    unsigned int tmpLen = strlen(tmp);
    char *output = OlaMallocAndCopyStr(tmp, tmpLen);
    if (output == NULL) {
        OlaLogError("get output failed", name);
        return NULL;
    }
    return output;
}

void OlaClearJsonString(cJSON *object)
{
    if (object == NULL) {
        return;
    }

    cJSON *c = object;
    while (c != NULL) {
        int value = c->type;
        unsigned int type = (unsigned int)value & cJSON_IsReference;
        if ((type == 0) && (c->child != NULL)) {
            OlaClearJsonString(c->child);
        }
        if ((type == 0) && (c->valuestring != NULL)) {
            unsigned int len = strlen(c->valuestring);
            (void)memset_s(c->valuestring, len, 0, len);
        }
        c = c->next;
    }
}

unsigned long long OlaDeltaTime(unsigned long long timeNew, unsigned long long timeOld)
{
    unsigned long long deltaTime;

    if (timeNew >= timeOld) {
        deltaTime = timeNew - timeOld;
    } else {
        deltaTime = (UINT64_MAX - timeOld) + timeNew + 1; /* 处理时间翻转 */
    }

    return deltaTime;
}

int OlaIsOvertime(bool *state, unsigned long long baseTime, unsigned long long maxTime)
{
    if (state == NULL) {
        OlaLogWarning("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }
    unsigned long long curTime = 0;
    int ret = OlaGetTime(&curTime);
    if ((ret != 0) || (curTime == 0)) {
        OlaLogError("get time error[%d]", ret);
        return OLA_OS_ERR_GET_TIME;
    }

    if (OlaDeltaTime(curTime, baseTime) > maxTime) {
        *state = true;
    } else {
        *state = false;
    }

    return OLA_OK;
}

char *OlaMallocAndCopyStr(const char *src, unsigned int srcLen)
{
    if ((src == NULL) || (srcLen == 0)) {
        OlaLogError("invalid src, len[%d]", srcLen);
        return NULL;
    }

    char *out = (char *)OlaMalloc(srcLen + 1);
    if (out == NULL) {
        OlaLogError("malloc, srcLen[%d]", srcLen);
        return NULL;
    }
    if (memset_s(out, srcLen + 1, 0, srcLen + 1) != OLA_OK) {
        OlaLogError("memset failed");
        OlaFree(out);
        return NULL;
    }
    if (strncpy_s(out, srcLen + 1, src, srcLen) != OLA_OK) {
        OlaLogError("strncpy failed");
        OlaFree(out);
        return NULL;
    }
    return out;
}

void OlaDataFree(OlaData *data)
{
    if (data == NULL) {
        return;
    }
    if (data->data != NULL) {
        OlaFree(data->data);
        data->data = NULL;
    }
    data->len = 0;
}

bool IsStrInvalid(const char *str, unsigned int minLen, unsigned int maxLen)
{
    if (str == NULL) {
        return false;
    }

    if ((strlen(str) > maxLen) || (strlen(str) < minLen)) {
        return false;
    }
    const char *p = str;
    while ((*p) != '\0') {
        if (isprint(*p)) {
            ++p;
        } else {
            return false;
        }
    }
    return true;
}

int OlaHexify(unsigned char *outBuf, unsigned int outLen,
    const unsigned char *inBuf, unsigned int inLen, bool upper)
{
    if ((outBuf == NULL) || (inBuf == NULL) || (outLen < (2 * inLen))) {
        OlaLogError("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    while (inLen > 0) {
        /* 对16取模得到高位 */
        unsigned char h = *inBuf / 16;
        /* 对16取余得到低位 */
        unsigned char l = *inBuf % 16;
        /* 数字10表示16进制字符a与其代表10进制数字的值 */
        if (h < 10) {
            *outBuf++ = '0' + h;
        } else {
            /* 数字10表示16进制字符a与其代表10进制数字的值 */
            *outBuf++ = (upper ? 'A' : 'a') + h - 10;
        }
        /* 数字10表示16进制字符a与其代表10进制数字的值 */
        if (l < 10) {
            *outBuf++ = '0' + l;
        } else {
            /* 数字10表示16进制字符a与其代表10进制数字的值 */
            *outBuf++ = (upper ? 'A' : 'a') + l - 10;
        }

        ++inBuf;
        inLen--;
    }
    return OLA_OK;
}